﻿#include "FITKOCCGeomTools.h"
#include "FITK_Interface/FITKInterfaceGeometry/FITKAbsVirtualTopo.h"
#include "FITK_Kernel/FITKCore/FITKAbstractDataManager.hpp"
#include "FITK_Kernel/FITKCore/FITKDataRepo.h"
#include "FITK_Interface/FITKInterfaceModel/FITKAbstractModel.h"
#include "FITKOCCVirtualTopoCreator.h"
#include <TopoDS_Vertex.hxx>
#include <TopoDS.hxx>
#include <BRep_Tool.hxx>

// Face
#include <TopoDS_Face.hxx>
#include <BRepAdaptor_Surface.hxx>
#include <gp_Pln.hxx>

namespace OCC
{
    Interface::FITKAbstractGeomPointTool* FITKOCCToolCreator::createPointTool()
    {
        //创建点工具
        return new FITKOCCPointTool;
    }

    Interface::FITKAbstractGeomFaceTool* FITKOCCToolCreator::createFaceTool()
    {
        //创建平面工具
        return new FITKOCCFaceTool;
    }

    // 点查询工具。
    //@{
    bool FITKOCCPointTool::getXYZ(const int & id, double* xyz, int modelId)
    {
        if (xyz == nullptr) return false;
        //数据类型转换
        Core::FITKAbstractDataObject* d = FITKDATAREPO->getDataByID(id);
        Interface::FITKAbsVirtualTopo* topo = dynamic_cast<Interface::FITKAbsVirtualTopo*>(d);
        return this->getXYZ(topo, xyz);
        
    }

    bool FITKOCCPointTool::getXYZ(Interface::FITKAbsVirtualTopo* topo, double* xyz, int modelId)
    {
        if (topo == nullptr || xyz == nullptr) return false;
        if (topo->getShapeType() != Interface::FITKGeoEnum::VSPoint) return false;
        //获取形状数据
        FITKOCCTopoShape* occShape = topo->getShapeT<FITKOCCTopoShape>();
        if (occShape == nullptr) return false;
        const TopoDS_Shape& shape = occShape->getTopoShape();
        //获取坐标
        TopoDS_Vertex vertex = TopoDS::Vertex(shape);
        gp_Pnt pt = BRep_Tool::Pnt(vertex);
        xyz[0] = pt.X(); xyz[1] = pt.Y(); xyz[2] = pt.Z();

        // Try to transform the point.
        Interface::FITKAbstractModel* model = FITKDATAREPO->getTDataByID<Interface::FITKAbstractModel>(modelId);
        if (model)
        {
            model->transformPoint(xyz, xyz);
        }

        return true;
    }
    //@}

    // 面查询工具。
    //@{
    bool FITKOCCFaceTool::getPlane(const int & id, double* pos, double* normal, double* up, int modelId)
    {
        if (!pos || !normal || !up)
        {
            return false;
        }

        // 数据类型转换
        Core::FITKAbstractDataObject* d = FITKDATAREPO->getDataByID(id);
        Interface::FITKAbsVirtualTopo* topo = dynamic_cast<Interface::FITKAbsVirtualTopo*>(d);
        return this->getPlane(topo, pos, normal, up);
    }

    bool FITKOCCFaceTool::getPlane(Interface::FITKAbsVirtualTopo* vtp, double* pos, double* normal, double* up, int modelId)
    {
        if (!vtp || !pos || !normal || !up)
        {
            return false;
        }

        // 获取抽象形状。
        FITKOCCTopoShape* occShape = vtp->getShapeT<FITKOCCTopoShape>();
        if (!occShape)
        {
            return false;
        }

        // 获取面。
        const TopoDS_Shape& shape = occShape->getTopoShape();
        TopoDS_Face face = TopoDS::Face(shape);
        if (face.IsNull())
        {
            return false;
        }

        // 判断是否为平面。
        BRepAdaptor_Surface BASur(face);
        GeomAbs_SurfaceType type = BASur.GetType();
        if (BASur.GetType() != GeomAbs_Plane)
        {
            return false;
        }

        // 获取信息。
        gp_Pln gpPln = BASur.Plane();
        gp_Pnt gpPos = gpPln.Location();
        gp_Dir gpNor = gpPln.Position().Direction();
        gp_Dir gpUp = gpPln.Position().YDirection();

        // 翻转。
        if (shape.Orientation() == TopAbs_REVERSED)
        {
            gpNor.Reverse();
            gpUp.Reverse();
        }

        // 返回数据。
        for (int i = 0; i < 3; i++)
        {
            pos[i] = gpPos.Coord(i + 1);
            normal[i] = gpNor.Coord(i + 1);
            up[i] = gpUp.Coord(i + 1);
        }

        // Try to transform the direction and point.
        Interface::FITKAbstractModel* model = FITKDATAREPO->getTDataByID<Interface::FITKAbstractModel>(modelId);
        if (model)
        {
            model->transformPoint(pos, pos);
            model->transformDirection(normal, normal);
            model->transformDirection(up, up);
        }

        return true;
    }
    //@}
}
